package org.nf.web.adapter;

import cn.hutool.core.util.ReflectUtil;
import org.nf.web.servlet.HandlerAdapter;
import org.nf.web.servlet.MethodArgumentsResolver;
import org.nf.web.servlet.ServletApi;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ServiceLoader;

/**
 * @Author czl
 * @Date 2024-05-15
 */
public class MethodHandierAdapter implements HandlerAdapter {
    /**
     * 加载参数解析器
     */
    private static final ServiceLoader<MethodArgumentsResolver> LOADER = ServiceLoader.load(MethodArgumentsResolver.class);


    @Override
    public boolean supports(Object handler) {
        //如果当前的handler是Method的实例则返回true
        return handler instanceof Method;
    }

    @Override
    public Object handler(Object handler, ServletApi servletApi) throws Exception {
        //将handler强转为method对象
        Method method = (Method) handler;
        //解析方法参数
        Object[] params = resolveMethodArguments(method, servletApi);
        //现获取声明method的Class
        Class<?> clazz = method.getDeclaringClass();
        //反射创建类实例
        Object instance = ReflectUtil.newInstance(clazz);
        //调用方法,并将请求和响应对象传给方法
        //返回视图对象给DispatcherServlet
        return method.invoke(instance, params);
    }

    /**
     * 解析方法参数，并转换为Object[]返回
     * @param method 处理请求的方法
     * @param servletApi 请求对象和响应对象，参数的数据内容都是从request中得到
     * @return 返回解析好的参数内容
     */
    private Object[] resolveMethodArguments(Method method, ServletApi servletApi) {
        //得到方法的所有参数信息
        Parameter[] parameters = method.getParameters();
        //创建一个Object[]，长度和Parameter[]保持一致，用于存放
        //参数的值
        Object[] values = new Object[parameters.length];
        //循环遍历得到每一个参数，将每一个参数交给参数解析器去解析
        //直到有一个合适参数解析器完成解析操作
        for(int i = 0; i < parameters.length; i++) {
            Parameter param = parameters[i];
            //循环参数解析器，找到匹配的Resolver
            for(MethodArgumentsResolver resolver : LOADER) {
                if(resolver.supports(param)) {
                    values[i] = resolver.resolve(param, servletApi);
                    break;
                }
            }
        }
        return values;
    }
}